//the coordinates of the shadow in light's eye space
varying vec4 shadowCoord;

//the shadow map texture
uniform sampler2DShadow shadowMap;

uniform int shadowKernelSize;

//the environment map
uniform samplerCube environmentMap;

uniform samplerCube diffuseMap;

//the interpolated normal
varying vec3 normal;

//the direction to the light
varying vec3 lightDir;

//the view direction to the vertex
varying vec3 viewDir;

float calculateShadow(int steps) {
  int i, j;
  vec2 offset;
  float result = 0.0;
  for (int i = 0; i < steps; i++) {
    for (int j = 0; j < steps; j++) {
      offset = vec2(i + 0.5 - steps * 0.5, j + 0.5 - steps * 0.5);
      offset /= 1024;
      offset *= shadowCoord.w;
      result += shadow2DProj(shadowMap, shadowCoord + vec4(offset, 0.0, 0.0)).w;
    }
  }
  result /= (steps * steps);
  return result;
}

void main() {
  vec3 n = normalize(normal);
  vec3 v = normalize(viewDir);
  vec3 ldir = normalize(lightDir);
  //must transform reflected direction back into world space
  vec3 r = gl_ModelViewMatrixInverse * vec4(reflect(v, n), 0);
  vec3 diffuseNormal = gl_ModelViewMatrixInverse * vec4(n, 0);

  float shadow = calculateShadow(shadowKernelSize);
  float lightObliquity = max(dot(n, ldir), 0.0);
  vec4 diffuseStrength = shadow * lightObliquity * 0.5 + textureCube(diffuseMap, diffuseNormal);
  
  vec4 specularStrength = textureCube(environmentMap, r);
  
  gl_FragColor = diffuseStrength * gl_FrontMaterial.diffuse + specularStrength * gl_FrontMaterial.specular;
}
